package com.gitee.ccr.use_pagehelper;

import lombok.Getter;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

@Getter
public class BasicExample {
    //只有'.'，','，'`'，空格，以及数字字母和下划线被允许
    private static final Pattern VALID_SQL_CHAR = Pattern.compile("[^.,` _0-9a-zA-Z]+");

    private boolean distinct;
    private String groupByClause;
    private String orderByClause;
    private String limitClause;
    private List<Criteria> orCriteria;

    public BasicExample() {
        this.orCriteria = new ArrayList<>();
    }

    public void setDistinct(boolean distinct) {
        this.distinct = distinct;
    }

    public void setGroupByClause(String groupByClause) {
        this.groupByClause = groupByClause.replaceAll(VALID_SQL_CHAR.pattern(), "");
    }

    public void setOrderByClause(String orderByClause) {
        this.orderByClause = orderByClause.replaceAll(VALID_SQL_CHAR.pattern(), "");
    }

    public void setLimitClause(String limitClause) {
        this.limitClause = limitClause.replaceAll(VALID_SQL_CHAR.pattern(), "");
    }

    public Criteria or() {
        Criteria criteria = new Criteria();
        orCriteria.add(criteria);
        return criteria;
    }


    /**
     * 通过 'or'链接的 where-clause
     * <p>
     * <p>
     * SQL: SELECT ... WHERE (a=1 and b=2) OR (a=1 and c=3)...
     */
    protected static class Criteria {
        private List<Criterion> criteria;

        private Criteria() {
        }

        public List<Criterion> getCriteria() {
            return this.criteria;
        }

        public boolean isValid() {
            return this.criteria.size() > 0;
        }

        //
        //
        private void addCriteria(String condition) {
            if (condition == null) {
                throw new RuntimeException("Value of condition cannot be null");
            }
            criteria.add(new Criterion(condition));
        }

        private void addCriteria(String condition, Object value, String property) {
            if (value == null) {
                throw new RuntimeException("Value for " + property + " cannot be null");
            }
            criteria.add(new Criterion(condition, value));
        }

        private void addCriteria(String condition, Object value, Object secondValue, String property) {
            if (value == null || secondValue == null) {
                throw new RuntimeException("Value for " + property + " cannot be null");
            }
            criteria.add(new Criterion(condition, value, secondValue));
        }

        //
        //no value
        public Criteria isNull(String field) {
            addCriteria(String.format("%s is null", field));
            return this;
        }

        public Criteria notNull(String field) {
            addCriteria(String.format("%s is not null", field));
            return this;
        }


        //single value
        public Criteria andEqual(String field, Object value) {
            addCriteria(String.format("%s =", field), value, field);
            return this;
        }

        public Criteria andGreaterThan(String field, Object value) {
            addCriteria(String.format("%s > ", field), value, field);
            return this;
        }

        public Criteria andIn(String field, List<Object> list) {
            addCriteria(String.format("%s in ", field), list, field);
            return this;
        }

        //between value
        public Criteria andBetween(String field, Object value, Object secondValue) {
            addCriteria(String.format("%s between", field), value, secondValue, field);
            return this;
        }

    }


    /**
     * 通过'and'连接的 where-clause
     * <p>
     * <p>
     * ...WHERE (a is not null) AND (b > 0) AND (c in (4,5,6,7)) AND (d between x and y)...
     * <p>
     * noValue--> condition: 'a is not null';
     * singleValue--> condition: 'b >'; value: 0;
     * listValue--> condition: 'c in'; value: [4,5,6,7]
     * betweenValue--> condition: 'd between'; value: x; secondValue: y;
     */
    @Getter
    private static class Criterion {
        private String condition;
        private Object value;
        private Object secondValue;

        private String typeHandler;

        private boolean noValue;
        private boolean singleValue;
        private boolean betweenValue;
        private boolean listValue;

        //no value
        private Criterion(String condition) {
            this.condition = condition;
            this.typeHandler = null;
            this.noValue = true;
        }

        //one value, single or list
        private Criterion(String condition, Object value, String typeHandler) {
            this.condition = condition;
            this.value = value;
            this.typeHandler = typeHandler;
            if (value instanceof List<?>) {
                this.listValue = true;
            } else {
                this.singleValue = true;
            }
        }

        private Criterion(String condition, Object value) {
            this(condition, value, null);
        }

        //between value
        private Criterion(String condition, Object value, Object secondValue, String typeHandler) {
            this.condition = condition;
            this.value = value;
            this.secondValue = secondValue;
            this.typeHandler = typeHandler;
            this.betweenValue = true;
        }

        private Criterion(String condition, Object value, Object secondValue) {
            this(condition, value, secondValue, null);
        }

    }


}
